/*
Copyright (C) 2008, 2009 Henrik Naess

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Text;
using MatrixPlugin.Constants;
using MatrixPlugin.Utils;
using ZoneFiveSoftware.Common.Data.Fitness;
using GpsRunningPlugin.Util;

namespace MatrixPlugin
{
    /// <summary>
    /// Contain a list of distance information for a point
    /// </summary>
    public class DistanceInfoList : List<DistanceInfo>
    {
        private DistanceInfoComparer m_sortOrder;

        #region Properties

        public DistanceInfoComparer SortOrder
        {
            //get { return _sortOrder; }
            set { m_sortOrder = value;
            this.Sort(m_sortOrder);
            }
        }

        //Must be sorted prior to getting the info, but no automatic sorting, as it may give unxpected changes
        public DistanceInfo Highest
        {
            get
            {
                DistanceInfo distanceInfo = null;
                if (Count > 0)
                {
                    //DistanceInfoList t = new DistanceInfoList();
                    //t.AddRange(this);
                    //t.Sort(_sortOrder);
                    //this.Sort(_sortOrder);
                    distanceInfo = this[0];
                }
                return distanceInfo;
            }
        }

        public DistanceInfo Median
        {
            get
            {
                DistanceInfo distanceInfo = null;
                if (Count > 0)
                {
                    //this.Sort(_sortOrder);
                    distanceInfo = this[((Count - 1) / 2)];
                }
                return distanceInfo;
            }
        }

        public DistanceInfo Lowest
        {
            get
            {
                DistanceInfo distanceInfo = null;
                if (Count > 0)
                {
                    //this.Sort(_sortOrder);
                    distanceInfo = this[Count - 1];
                }
                return distanceInfo;
            }
        }

        #endregion

        //public string AllAsToolTip(string baseActivityReferenceId, string formatString)
        //{
        //   return AllAsToolTipDelimitedFormated(baseActivityReferenceId, formatString, "");
        //}

        public string AllAsToolTipDelimitedFormated(string baseActivityReferenceId, string formatString,
                                                    string delimiter)
        {
            StringBuilder tooltipLines = new StringBuilder();
            for (int distanceIterator = 0; distanceIterator < Count; distanceIterator++)
            {
                DistanceInfo info = this[distanceIterator];
                string tooltip;
                try
                {
                    tooltip = GetToolTipForActivity(info, Count > 1, formatString, baseActivityReferenceId, delimiter);
                }
                catch (Exception) //TODO: More narrow exception?
                {
                    // Use default tooltip if the one if preferences fails for some reason
                    tooltip = GetToolTipForActivity(info, Count > 1, Preferences.TooltipFormatString,
                                                    baseActivityReferenceId, delimiter);
                }

                tooltipLines.AppendLine(tooltip);
            }

            return tooltipLines.ToString();
        }
        public IDictionary<string, string> AllActivitiesAsToolTipDictonary(string baseActivityReferenceId, string formatString,
                                                    string delimiter)
        {
            IDictionary<string, string> result = new Dictionary<string, string>();

            for (int distanceIterator = 0; distanceIterator < Count; distanceIterator++)
            {
                DistanceInfo info = this[distanceIterator];
                string tooltip;
                try
                {
                    tooltip = GetToolTipForActivity(info, Count > 1, formatString, baseActivityReferenceId, delimiter);
                }
                catch (Exception) // More narrow exception?
                {
                    // Use default tooltip if the one if preferences fails for some reason
                    tooltip = GetToolTipForActivity(info, Count > 1, Preferences.TooltipFormatString,
                                                    baseActivityReferenceId, delimiter);
                }
                //Only one activity returned, the activities could be counted
                string key = info.ActivityId;
                if (!result.ContainsKey(key))
                {
                    result.Add(key, tooltip);
                    break;
                }
            }
            return result;
        }

        private static string GetToolTipForActivity(DistanceInfo info, bool moreThanActivity, string tooltip,
                                                    string baseActivityReferenceId, string delimiter)
        {
            return GetToolTipForActivity(info.Activity, info.Activity.Name, info.ActivityId,
            info.Distance, info.EffectiveTime, info.StartTimeLocalTime,
            info.HasElevation, info.ElevationInMeters,
            info.IncludeStoppedTime, info.StoppedTimeAsFormattedString,
            moreThanActivity, tooltip, baseActivityReferenceId, delimiter);
        }
        public static string GetToolTipForActivity(IActivity Activity, string activityName, string activityReferenceId, 
            float Distance, TimeSpan EffectiveTime, DateTime StartTimeLocalTime, 
            bool HasElevation, float ElevationInMeters,
            bool IncludeStoppedTime, string StoppedTimeAsFormattedString,
            bool moreThanActivity, string tooltip,
                                                    string baseActivityReferenceId, string delimiter)
        {
            if (string.IsNullOrEmpty(tooltip))
            {
                tooltip = Settings.ToolTipFormatString;
            }
            ModifyToolTip("{time", (EffectiveTime-TimeSpan.FromMilliseconds(EffectiveTime.Milliseconds)).ToString(), delimiter, ref tooltip);
            ModifyToolTip("{avgspeed", UnitUtil.Speed.ToString(Distance/EffectiveTime.TotalSeconds, Activity, "u"), delimiter,
                          ref tooltip);
            ModifyToolTip("{avgpace", UnitUtil.Pace.ToString(Distance/EffectiveTime.TotalSeconds, Activity, "u"), delimiter,
                          ref tooltip);
            ModifyToolTip("{starttime", StartTimeLocalTime.ToString(), delimiter, ref tooltip);
            ModifyToolTip("{name", activityName, delimiter, ref tooltip);
            ModifyToolTip("{distance", UnitUtil.Distance.ToString(Distance, "u"), delimiter, ref tooltip);
            if (HasElevation)
                ModifyToolTip("{elevation", UnitUtil.Elevation.ToString(ElevationInMeters, "u"), delimiter,
                              ref tooltip);
            else
                ModifyToolTip("{elevation", null, delimiter, ref tooltip);

            if (IncludeStoppedTime)
                ModifyToolTip("{stoppedtime", StoppedTimeAsFormattedString, delimiter, ref tooltip);
            else
                ModifyToolTip("{stoppedtime", null, delimiter, ref tooltip);

            tooltip = tooltip.TrimEnd();
            if (moreThanActivity && baseActivityReferenceId == activityReferenceId)
                tooltip += " ***" + delimiter;
            else
                tooltip += delimiter;

            return tooltip;
        }

        private static void ModifyToolTip(string field, string value, string delimiter, ref string tooltip)
        {
            int fieldStartPos = tooltip.IndexOf(field);
            if (fieldStartPos >= 0)
            {
                int padding = 0;
                int fieldEndPos = tooltip.Substring(fieldStartPos).IndexOf("}");
                if (fieldEndPos > field.Length)
                {
                    string s = tooltip.Substring(fieldStartPos + field.Length + 1, fieldEndPos - field.Length - 1);
                    padding = Convert.ToInt16(s);
                }

                if (padding == 0)
                {
                    if (delimiter.Length > 0)
                        tooltip = tooltip.Replace(field + "}", value + delimiter);
                    else
                        tooltip = tooltip.Replace(field + "}", value);
                }
                else
                {
                    string textToReplace = field +
                                           tooltip.Substring(fieldStartPos + field.Length,
                                                             fieldEndPos - field.Length + 1);
                    if (delimiter.Length > 0)
                        tooltip = tooltip.Replace(textToReplace, value + delimiter);
                    else
                        tooltip = tooltip.Replace(textToReplace, PadString(value, padding));
                }
            }
        }

        private static string PadString(string text, int padLength)
        {
            if (text != null)
            {
                text = text.Substring(0, text.Length > padLength ? padLength : text.Length);
                if (padLength > 0)
                    text = text.PadRight(padLength);
                else if (padLength < 0)
                    text = text.PadLeft(padLength);
            }

            return text;
        }

        public DistanceInfo FindActivity(string id)
        {
            //Note: The list is sorted just prior to this loop, why foreach will give exception: Collection modified
            for(int i=this.Count-1; i>=0; i--)
            {
                if (id == this[i].ActivityId)
                    return this[i];
            }
            return null;
        }

        public static DistanceInfo GetDistanceInfoForTempoType(DistanceInfoList distanceInfoList, General.TempoType calcDataType)
        {
            DistanceInfo distanceInfo;
            switch (calcDataType)
            {
                case General.TempoType.Slowest:
                    distanceInfo = distanceInfoList.Lowest;
                    break;
                case General.TempoType.Median:
                    distanceInfo = distanceInfoList.Median;
                    break;
                case General.TempoType.Fastest:
                    distanceInfo = distanceInfoList.Highest;
                    break;
                default:
                    throw new PlugInException("Unknown sort parameter");
            }
            return distanceInfo;
        }
    }
}